/**********************************************************************
 *@Project： clouddemo
 *@File: ShiroConfig
 *@Date: 2017/4/15
 *@Copyright (C) 2013-2017 http://www.posun.cn. All Rights Reserved.
 ***********************************************************************
 *注意： 本内容仅限于深圳市普盛实业有限公司内部使用，禁止外泄
 **********************************************************************
 */
package com.posun.edu.system.config;

import com.google.common.collect.Maps;
import com.posun.cloud.tracing.enums.LogType;
import com.posun.framework.log.AresLog;
import com.posun.framework.log.LogBuilder;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.AnonymousFilter;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.filter.authc.LogoutFilter;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.scheduling.TaskScheduler;

import javax.servlet.Filter;
import java.util.Map;

/**
 * <p>
 * TODO<br>
 *
 * @author YL
 * @version 1.0
 * @createTime 2017/4/15 14:53
 * @ChangeLog
 */
@Configuration
public class ShiroConfig {

    @Autowired
    public TaskScheduler taskScheduler;

    @Bean
    @DependsOn(value = "lifecycleBeanPostProcessor")
    public UserRealm userRealm(CacheManager cacheManager, HashedCredentialsMatcher hashedCredentialsMatcher) {
        UserRealm realm = new UserRealm();
        realm.setCacheManager(cacheManager);
        //realm.setCredentialsMatcher(hashedCredentialsMatcher);
        realm.setCachingEnabled(true);
        realm.setAuthenticationCachingEnabled(true);
        realm.setAuthenticationCacheName("ares_shiro:authenticationCache:");
        realm.setAuthorizationCachingEnabled(true);
        realm.setAuthorizationCacheName("ares_shiro:authorizationCache:");
        return realm;
    }

    /**
     * 凭证匹配器
     *
     * @param cacheMananger
     *
     * @return
     */
    @Bean(name = "credentialsMatcher")
    public HashedCredentialsMatcher hashedCredentialsMatcher(CacheManager cacheMananger) {
        // 密码错误重试次数
        RetryLimitHashedCredentialsMatcher r = new RetryLimitHashedCredentialsMatcher(cacheMananger);
        r.setHashAlgorithmName("md5");//MD5散列算法
        r.setHashIterations(2);//散列次数
        r.setStoredCredentialsHexEncoded(true);
        return r;
    }

    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager, ShiroProperties shiroProperties) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // 必须设置 SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
        shiroFilterFactoryBean.setLoginUrl(shiroProperties.getLoginUrl());
        // 登录成功后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl(shiroProperties.getSuccessUrl());
        // 未授权界面;
        shiroFilterFactoryBean.setUnauthorizedUrl(shiroProperties.getUnauthorizedUrl());

/*        //从数据库获取
        List<Dict> list = dictService.findAll();

        for (Dict dict : list) {
            filterChainDefinitionMap.put(dict.getId(), "anon");
        }*/

        Map<String, Filter> filters = Maps.newHashMap();
        filters.put("anon", new AnonymousFilter());
        filters.put("authc", new FormAuthenticationFilter());
        LogoutFilter logoutFilter = new LogoutFilter();
        logoutFilter.setRedirectUrl(shiroProperties.getLoginUrl());
        filters.put("logout", logoutFilter);
        shiroFilterFactoryBean.setFilters(filters);

        Map<String, String> chains = Maps.newLinkedHashMap();
        chains.put(shiroProperties.getLoginUrl(), "anon");
        chains.put(shiroProperties.getUnauthorizedUrl(), "anon");
        chains.put("/sso/logout", "logout");
        chains.put("/static/**", "anon");
        chains.put("/public/**", "anon");
        chains.put("/monitor/**", "anon");
        chains.put("/webjars/**", "anon");
        //swagger配置
        chains.put("/swagger**", "anon");
        chains.put("/v2/api-docs", "anon");
        chains.put("/swagger-resources/configuration/ui", "anon");

        chains.put("/**", "user");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(chains);
        System.out.println("Shiro拦截器工厂类注入成功");
        return shiroFilterFactoryBean;
    }

    /**
     * 使用自定义的shiroRedisSession
     *
     * @param cacheManager
     * @param userRealm
     *
     * @return
     */
    @Bean
    public SecurityManager securityManager(CacheManager cacheManager, SessionManager sessionManager,
                                           UserRealm userRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 设置realm.
        securityManager.setRealm(userRealm);
        // 自定义缓存实现 使用redis
        securityManager.setCacheManager(cacheManager);
        // 自定义session管理 使用redis
        securityManager.setSessionManager(sessionManager);
        return securityManager;
    }

    /*public KickoutSessionControlFilter kickoutSessionControlFilter(){
        KickoutSessionControlFilter kickoutSessionControlFilter = new KickoutSessionControlFilter();
        //使用cacheManager获取相应的cache来缓存用户登录的会话；用于保存用户—会话之间的关系的；
        //这里我们还是用之前shiro使用的redisManager()实现的cacheManager()缓存管理
        //也可以重新另写一个，重新配置缓存时间之类的自定义缓存属性
        kickoutSessionControlFilter.setCacheManager(cacheManager());
        //用于根据会话ID，获取会话进行踢出操作的；
        kickoutSessionControlFilter.setSessionManager(sessionManager());
        //是否踢出后来登录的，默认是false；即后者登录的用户踢出前者登录的用户；踢出顺序。
        kickoutSessionControlFilter.setKickoutAfter(false);
        //同一个用户最大的会话数，默认1；比如2的意思是同一个用户允许最多同时两个人登录；
        kickoutSessionControlFilter.setMaxSession(1);
        //被踢出后重定向到的地址；
        kickoutSessionControlFilter.setKickoutUrl("/kickout");
        return kickoutSessionControlFilter;
    }*/

    /**
     * 开启shiro aop注解支持.使用代理方式;所以需要开启代码支持;<br/>
     * 拦截@ RequiresPermissions注释的方法
     *
     * @param securityManager
     *
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        AresLog.info(new LogBuilder(ShiroConfig.class, LogType.DEBUG, "初始化ShiroConfiguration.authorizaionAttributeSourceAdvisor"));
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

}
